36. SpringMVC1 - RequestMapping, 接收参数, 获取 Servlet API, 处理响应

简单示例

  1. 导入 Jar 包

    1
    2
    3
    4
    5
    6
    7
    8
    spring-aop-4.0.0.RELEASE.jar
    spring-beans-4.0.0.RELEASE.jar
    spring-context-4.0.0.RELEASE.jar
    spring-core-4.0.0.RELEASE.jar
    spring-expression-4.0.0.RELEASE.jar
    commons-logging-1.1.3.jar
    spring-web-4.0.0.RELEASE.jar // 核心
    spring-webmvc-4.0.0.RELEASE.jar // 核心
  2. 配置 web.xml

    需要在 web.xml 中配置 SpringMVC 的核心(前端)控制器 DispatherServlet。作用是加载 SpringMvc 的配置文件。如下的配置方式 DispatcherServlet 会自动加载配置文件,此时的配置文件有默认的位置和名称。默认位置是 WEB-INF 下,默认名称是 <servlet-name> 的名称加 -servlet.xml。例如以下配置文件的名称为 springMvc-servlet.xml。当加载了配置文件后,SpringMVC 就会扫描组件找到控制层。

servlet.xml 配置如下

1
2
3
4
5
6
7
8
9
<servlet>
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

SpringMVC 配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

<!-- 扫描组件,将加上@Controller注解的类作为springMVC的控制层 -->
<context:component-scan base-package="com.atguigu.controller"></context:component-scan>

<!--
配置视图解析器
作用:将prefix + 视图名称 + suffix 确定最终要跳转的页面
/WEB-INF/view/success.jsp
-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>

  1. 创建 POJO(普通的 java 类),在此类上加上 @Controller 注解,SpringMVC 就会将此类作为控制层进行加载,让其处理请求相应。

  2. 在控制层中,需要在方法上设置 @RequestMapping(value="路径")注解。 SpringMVC 就是将此路径与控制层中的方法进行匹配。此时请求路径为 localhost:8080/项目名/路径

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package com.atguigu.test;

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;

    @Controller
    public class TestController {
    @RequestMapping("hello")
    public String hello() {
    return "success";
    }
    }
  3. 处理请求的方法会返回一个字符串,即视图名称。最终通过配置文件中的视图解析器实现页面跳转。即 prefix + 视图名称 + suffix 为最终跳转的页面路径。

代码地址

RequestMapping

支持的属性

  1. @RequestMapping 可以加上类上,也可以加在方法上。表示设置请求映射,把请求和控制层中的方法设置映射关系。当请求路径和 value 属性值一致时,则该注解所标注的方法就是处理请求的方法。
  2. value 属性可以设置访问地址。
  3. method 属性可以设置请求方式。
  4. params 和 headers支持简单的表达式:param1: 表示请求必须包含名为 param1 的请求参数。!param1: 表示请求不能包含名为 param1 的请求参数。param1 != value1: 表示请求包含名为 param1 的请求参数,但其值不能为 value1。{“param1=value1”, “param2”}: 请求必须包含名为 param1 和param2 的两个请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.atguigu.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;


@Controller
@RequestMapping("/mvc")
public class TestController {
/**
* @RequestMapping:可以加上类上,也可以加在方法上。表示设置请求映射,把请求和控制层中的方法设置映射关系。
* 当请求路径和 @RequestMapping 的 value 属性值一致时,则该注解所标注的方法就是处理请求的方法
* value 属性可以设置访问地址。
* method 属性可以设置请求方式。
* params 和 headers支持简单的表达式:
* param1: 表示请求必须包含名为 param1 的请求参数
* !param1: 表示请求不能包含名为 param1 的请求参数
* param1 != value1: 表示请求包含名为 param1 的请求参数,但其值不能为 value1
* {"param1=value1", "param2"}: 请求必须包含名为 param1 和param2 的两个请求
*/

@RequestMapping(value = "test", method = RequestMethod.POST)
public String testPOST() {
return "success";
}

@RequestMapping(
value = "test",
method = RequestMethod.GET,
params = {"username", "age!=12"},
headers = {"Content-Type!=text/css"}
)
public String testGET() {
return "success";
}
}

Ant 风格资源地址

1
2
3
?:匹配文件名中的一个字符
*:匹配文件名中的任意字符
**:匹配多层路径

例子:

1
2
3
4
5
6
7
8
/user/*/createUser
匹配 /user/aaa/createUser、/user/bbb/createUser 等 URL

/user/**/createUser
匹配 /user/createUser、/user/aaa/bbb/createUser 等 URL

/user/createUser??
匹配 /user/createUseraa、/user/createUserbb 等 URL

PathVariable

@PathVariable 注解可以将请求URL路径中的请求参数,传递到处理请求方法的入参中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.atguigu.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;


@Controller
@RequestMapping("/mvc")
public class TestController {
@RequestMapping(value = "/test/{id}/{username}", method = RequestMethod.GET)
public String testGET(@PathVariable("id") Integer id, @PathVariable("username") String username) {
System.out.println("id:" + id + ", username:" + username);
return "success";
}
}

HiddenHttpMethodFilter

浏览器 form 表单只支持 GET 与 POST 请求,而DELETE、PUT 等 method 并不支持,Spring3.0 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使得支持 GET、POST、PUT 与 DELETE 请求。即,若是 POST 请求,且参数中有 _method 参数,那么经过转换真正的请求方式就是 _method 的值。

源码分析

指定参数为 _method

hiddenHttpMethodFilter 获取 _method 参数的值。并判断如果是 POST 请求,且 _method 的值不为空

那么就将 POST 请求替换为 _method 的值的请求(getMethod的返回值为 _method 的值。即将请求方式进行转换)

REST CRUD 操作

web.xml 新增 HiddenHttpMethodFilter 配置

1
2
3
4
5
6
7
8
9
 <!-- 配置 HiddenHttpMethodFilter -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="testREST/1001">测试 GET 请求</a>

<hr/>
<form action="testREST" method="POST">
<input type="submit" value="测试 POST 请求"/>
</form>

<hr/>
<form action="testREST" method="POST">
<input type="hidden" name="_method" value="PUT"/>
<input type="submit" value="测试 PUT 请求"/>
</form>

<hr/>
<form action="testREST/1001" method="POST">
<input type="hidden" name="_method" value="DELETE"/>
<input type="submit" value="测试 DELETE 请求"/>
</form>

</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.atguigu.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class RESTController {

@RequestMapping("/testREST/{id}")
public String name(@PathVariable("id") String id) {
System.out.println("GET, ID=" + id);
return "success";
}

@RequestMapping(value = "/testREST", method = {RequestMethod.POST})
public String name1() {
System.out.println("POST");
return "success";
}

@RequestMapping(value = "/testREST", method = {RequestMethod.PUT})
public String name2() {
System.out.println("PUT");
return "success";
}

@RequestMapping(value = "/testREST", method = {RequestMethod.DELETE})
public String name3(@PathVariable("id") String id) {
System.out.println("DELETE");
return "success";
}
}

获取客户端数据

RequestParam

在处理请求的方法中,加入相对应的形参,保证形参参数名和传递的数据的参数名保持一致,就可以自动赋值。 如果形参参数名和传递的数据的参数名不一致,那么可以使用 @RequestParam 进行设置。

@RequestParam 可以设置 required 属性。表示该参数是否必须。

@RequestParam 可以设置 defaultValue 属性。表示该参数没传递时(注意是没传递,也就是 null 的时候,并不是为空字符串的时候)的默认值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="param" method="POST">
<!-- username: <input type="text" name="name"/> <br/> --> <!-- 不传递 name 参数 -->
password: <input type="text" name="password"/> <br/>
age: <input type="text" name="age"/> <br/>
address: <input type="text" name="address"/> <br/>
province: <input type="text" name="province"/> <br/>
city: <input type="text" name="city"/> <br/>
country: <input type="text" name="country"/> <br/>
<input type="submit" value="添加"/>
</form>

<hr/>
<form action="param2" method="POST">
<input type="submit" value="添加"/>
</form>

<hr/>
<form action="param3" method="POST">
<input type="submit" value="添加"/>
</form>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.atguigu.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class ParamController {
/**
* 保证形参参数名和传递的数据的参数名保持一致,就可以自动赋值 (下面的 password,age 就是)。
* 如果形参参数名和传递的数据的参数名不一致,那么可以使用 @RequestParam 进行设置(下面的 username 就是)。
* @RequestParam 可以设置 required 属性。表示该参数是否必须。
* @RequestParam 可以设置 defaultValue 属性。表示该参数没传递时(注意是没传递,也就是 null 的时候,并不是为空字符串的时候)的默认值。
*/
@RequestMapping(value = "/param", method = {RequestMethod.POST})
public String param(@RequestParam(value = "name", required = false, defaultValue = "admin") String username, String password, String age) {
System.out.println(" username:" +username + " password:" + password + " age:" + age);
return "success";
}
}

RequestHeader

1
2
3
4
5
6
7
8
9
 /**
* 使用 @RequestHeader 获取请求头信息
* @RequestHeader@RequestParam 一样,也有 required 属性和 defaultValue 属性
*/
@RequestMapping(value = "/param2", method = {RequestMethod.POST})
public String param2(@RequestHeader(value = "User-Agent") String userAgent) {
System.out.println("User-Agent: " + userAgent);
return "success";
}

CookieValue

1
2
3
4
5
6
7
8
9
 /**
* 使用 @CookieValue 获取 cookie 信息
* @CookieValue@RequestHeader@RequestParam 一样,也有required属性和defaultValue 属性
*/
@RequestMapping(value = "/param3", method = {RequestMethod.POST})
public String param3(@CookieValue(value = "JSESSIONID") String sessionId) {
System.out.println("sessionId: " + sessionId);
return "success";
}

POJO 作为参数

新建 User 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.atguigu.bean;

public class User {
private Integer id;
private String username;
private String password;
private Integer age;
private Address address;

public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + ", age=" + age + ", address="
+ address + "]";
}
public User(Integer id, String username, String password, Integer age, Address address) {
super();
this.id = id;
this.username = username;
this.password = password;
this.age = age;
this.address = address;
}
public User() {
super();
}
}

新建 Address 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.atguigu.bean;

public class Address {
private String province;
private String city;
private String country;

public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
@Override
public String toString() {
return "Address [province=" + province + ", city=" + city + ", country=" + country + "]";
}
}

表单提交值

1
2
3
4
5
6
7
8
9
<form action="param4" method="post">
username:<input type="text" name="username" /><br />
password:<input type="text" name="password" /><br />
age:<input type="text" name="age" /><br />
province:<input type="text" name="address.province" /><br />
city:<input type="text" name="address.city" /><br />
country:<input type="text" name="address.country" /><br />
<input type="submit" value="添加" />
</form>

Controller 获取

可以使用 POJO 获取客户端数据,要求实体类对象中的属性名一定要和页面中表单元素的 name 属性值一致。且支持级联关系

1
2
3
4
5
6
7
8
9
 /**
* 可以使用 POJO 获取客户端数据,要求实体类对象中的属性名一定要和页面中表单元素的 name 属性值一致。且支持级联关系
*
*/
@RequestMapping(value = "/param4", method = {RequestMethod.POST})
public String param4(User user) {
System.out.println("user: " + user);
return "success";
}

获取 Servlet API

可以通过形参的方式获取 Servlet API。支持 HttpServletRequest,HttpServletResponse,HttpSession, java.security.Principal,Locale,InputStream,OutputStream,Reader,Writer。

1
2
3
4
5
6
7
8
/**
* 可以通过形参的方式获取 Servlet API
*/
@RequestMapping(value = "/param4", method = {RequestMethod.POST})
public String param4(User user, HttpServletRequest request) {
System.out.println(request.getParameter("username"));
return "success";
}

处理响应

SpringMVC 处理请求过程中,往作用域中放值有三种方式。分别是 ModelAndView,Model 和 Map。不管是哪种方式最终都会转换为 ModelAndView 的方式进行处理。

ModelAndView

控制器处理方法的返回值如果为 ModelAndView, 则其既包含视图信息,也包含模型数据信息。

1
2
3
4
5
6
7
@RequestMapping(value = "/param5", method = {RequestMethod.POST})
public ModelAndView param5(User user) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("username", user.getUsername()); // 往 request 作用域中赋值
modelAndView.setViewName("success"); // 设置 view 名称
return modelAndView;
}

Map

1
2
3
4
5
6
7
8
/**
* 往作用域传值方式2
*/
@RequestMapping(value = "/param6", method = {RequestMethod.POST})
public String param6(Map<String, Object> map) {
map.put("username", "admin"); // 往 request 作用域中赋值
return "success"; // 返回视图名称
}

Model

1
2
3
4
5
6
7
8
/**
* 往作用域传值方式3
*/
@RequestMapping(value = "/param7", method = {RequestMethod.POST})
public String param7(Model model) {
model.addAttribute("username", "admin"); // 往 request 作用域中赋值
return "success"; // 返回视图名称
}

代码地址